package fschmidt.util.java;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentHashMap;


public final class Interner<T>  {

	private static final class MyReference<T> extends WeakReference<T> {
		private final int hash;

		MyReference(T t,ReferenceQueue<T> q) {
			super(t,q);
			hash = t.hashCode();
		}

		public boolean equals(Object obj) {
			if( this==obj )
				return true;
			if( !(obj instanceof MyReference) )
				return false;
			MyReference ref = (MyReference)obj;
			T t = this.get();
			if( t==null )
				return false;
			return t.equals(ref.get());
		}

		public int hashCode() {
			return hash;
		}
	}

	private final ConcurrentMap<MyReference<T>,MyReference<T>> map = new ConcurrentHashMap<MyReference<T>,MyReference<T>>();
	private ReferenceQueue<T> queue = new ReferenceQueue<T>();

	private void sweep() {
		while(true) {
			Reference<? extends T> ref = queue.poll();
			if( ref == null )
				return;
			map.remove(ref);
		}
	}

	public T intern(T t) {
		MyReference<T> ref = new MyReference<T>(t,queue);
		while(true) {
			MyReference<T> ref2 = map.putIfAbsent(ref,ref);
			if( ref2 == null ) {
				sweep();
				return t;
			}
			T t2 = ref2.get();
			if( t2 != null ) {
				ref.clear();
				return t2;
			}
		}
	}

}
